身につけておきたいWebサイト高速化テクニック #6|HTTPリクエスト数削減テクニック02:CSS Sprite編
はじめに
前回はHTTPリクエスト数削減テクニック01「インラインイメージ編」でData URI Encodeを使ったインラインイメージについて紹介しました。この記事をより深く解説した記事をCodezineさまに寄稿したのでこちらも参考にしてください。
今回はHTTPリクエスト数削減テクニックの2つ目「CSS Sprite」について解説します。
すでに定番のテクニックとなっていますが何となく使っていることが多いと思うので復習と思って読んでみてはいかがでしょうか。これから覚える方にも向けて丁寧に解説したいと思います。
1,CSS Spriteとは
CSS Spriteとは、アイコンやナビゲーションなどのいくつかの画像を1つにまとめた画像(以降Sprite画像とする)を用意し、CSSのbackgroundプロパティを使って一部分のイメージだけを見せるテクニックです。代表的なSprite画像の例がYoutubeで使われているここの画像です。
1.1,CSS Spriteの原理
原理についてもう少し詳しく紹介したいと思います。
CSS SpriteはCSSのbackground-imageとbackground-positionプロパティを使って表示を制御します。Sprite画像の個々の画像の座標(左上の0:0を起点に個々の画像の起点を計算します)をbackground-positionに値を設定することで背景画像の位置を調整するというわけです。
高解像度ディスプレイ(Retina Display)の場合
昨今スマートフォンへの対応も増えており画像も綺麗に表示したいという要望も多いと思います。高解像度ディスプレイへの対応も基本的な原理は通常のCSS Spriteの原理と同じです。解像度に合わせた画像(iPhoneであれば2倍)を用意することはもちろんですが、1つbackground-sizeというCSSプロパティを追加で指定します。
background-sizeプロパティは背景画像のサイズをCSS上でどのように扱うか指定するためのプロパティです。このプロパティを指定することで解像度をあまり意識すること無くbackground-positionの指定を行うことができます。
1.2,CSS Spriteの作業と流れ
CSS Spriteにはこのような工程があります。
実際にやってみるとわかりますが、すべて手作業で行うと非常に手間なことが多いです。後ほど簡単に紹介しますがCSS Spriteは自動化するツールを使った方が良いでしょう。
- 1つにまとめる画像を選定する
- Sprite画像用のデザインファイルを作る
- 画像の配置を考える
- 配置を決定し座標をメモする
- CSS Sprite画像を書き出す
- background-imageにSprite画像を指定する
- background-positionで座標指定を行う
また、Sprite画像を作成するにあたり、少し頭を使います。
例えば、ある幅や高さが可変な要素に対してCSS Spriteを使う場合、Sprite画像内の要素を縦方向・横方向どちらか一方だけに並べたりどちらの方向にも対応するために斜めに配置したりなど描画に合わせて配置や画像間の余白を計算する必要があります。CSS3の普及により背景画像をアニメーションさせることもあるので、この場合も配置を考えなければならないでしょう。コーディングを経験しているデザイナー、コーダーであればこの点に注意しなければならないことがわかると思います。
2,CSS Spriteの記述方法
続いて、CSSにはどのような記述を行えばCSS Spriteが実現できるのかを紹介します。
2.1,HTMLの構造
横に並ぶ4つのナビゲーションがあると仮定します。HTMLの構造は以下の通りです。このナビゲーションの個々の要素に対してCSS Spriteを指定します。サンプルとして240px × 40pxが4 × 4で並んだ横幅960px縦幅160pxのSprite画像を用意しました。
<nav> <ul id="globalNav"> <li class="item item01"><a href="#">01</a></li> <li class="item item02"><a href="#">02</a></li> <li class="item item03"><a href="#">03</a></li> <li class="item item04"><a href="#">03</a></li> </ul> </nav>
2.2,CSSの指定
CSSでは.itemにSprite画像の指定を行い.item0Xに対してbackground-positionを使った座標の制御を行います。これだけでは、サイズを指定していないので内包するテキストなどによって背景がうまく表示されないので、横幅と高さを加えます。さらにアンカータグはインライン要素ですからこちらもdisplay: block;を加えてブロック要素にしましょう。
さらに、:hoverや:activeなどの疑似要素に合わせて座標をづらしていきます。
.item a { display: block; width: 240px; height: 40px; background-image: url('sprite.png'); background-repeat: no-repeat; } .item01 a { background-position: 0px 0px; } .item01 a:hover { background-position: 0px -40px; } .item01 a:active { background-position: 0px -80px; } .item01.current a { background-position: 0px -120px; }
座標指定は左上を起点にx軸・y軸の移動距離を指定するので値はマイナス指定になるので注意しましょう。
このままでは内包するテキストや要素によってはみ出してしまう可能性があるのでoverflow: hidden;を指定しておきます。
.item a { overflow: hidden; display: block; width: 240px; height: 40px; background-image: url('sprite.png'); background-repeat: no-repeat; }
画像置換について
このままでは背景画像の上に文字が表示されてしまうので、文字を表示させたくない場合、画像置換というテクニックが使われています。昔からあるテクニックですが、数年前にはtext-indent: -9999px;を使った方法がよく使われていました。現在ではtext-indent: 100%;とwhite-space: nowrap;を使う方がスマートです。基本的に画像置換を行っても問題ないと思いますが、Googleのコンテンツに関するガイドラインに隠しテキストと隠しリンクという項があります。この点には注意してください。
.item a { overflow: hidden; display: block; width: 240px; height: 40px; background-image: url('sprite.png'); background-repeat: no-repeat; text-indent: 100%; white-space: nowrap; }
Retina ディスプレイの対応方法
Retinaディスプレイに対応するにはMedia Queriesを使ってデバイスに合わせてCSS Spriteの指定を振り分けます。このナビゲーションの場合、倍のサイズ1920px × 320pxとなります。これをMedia Queries内のbackground-imageに指定します。
.item a { overflow: hidden; display: block; width: 240px; height: 40px; background-image: url('sprite.png'); background-repeat: no-repeat; text-indent: 100%; white-space: nowrap; } @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) { .item a { background-image: url('sprite_x2.png'); background-size: 960px 160px; } }
background-sizeが指定されていない場合、@media規則内のbackground-positionは1920px × 320pxの座標を前提として計算されますが、background-sizeを指定することにより通常時と同じbackground-positionで扱うことができます。
Retina ディスプレイに対応したCSS Spriteのサンプル
こちらのイメージではありませんが、実際に表示を確認できるサンプルとソースコードをこちらで公開しています。
3,CSS Spriteのメリット
例えばWebサイトのナビゲーションが5つあるとしましょう。その1つ1つのボタンに対してリンクの状態に合わせたnormal/hover/activeの3つ、さらに現在いる場所を表すcurrnetを足した4つの状態があるとすると合計で20個の画像になります。単純に考えると20回のHTTPリクエストが行われることになります。これだけHTTPリクエストが多いと、ブラウザの同時接続数の制限によりリクエストがブロックされ遅延の原因となります。このような場合にCSS Spriteを使って画像を1つにすることでHTTPリクエスト数を大きく削減できます。
3.1,個別に読み込んだときのウォーターホールチャート
別々に10個の画像をロードした場合に同時接続数の制限によりWaitingの時間が増加し、Load Eventまでに時間がかかっています。
3.2,CSS Spriteを使ったときのウォーターホールチャート
同時接続数の制限に引っかからないのでファイルサイズが大きかったとしてもWaitingが無くなり個別に読み込んだときよりLoad Eventが早く起きています。
4,CSS Spriteのデメリット
CSS Spriteはパフォーマンスの向上も大いに見込め、非常に良いテクニックに見えますがいくつかのデメリットもあります。
4.1,一部の背景画像だけリピートすることができない
一般的に多くの画像をまとめているSprite画像で一部の画像をリピートしたくてもできません。無理にCSS Spriteで背景画像をリピートさせる必要はないのでリピートを行いたい場合はインラインイメージと併用すると良いでしょう。
4.2,Sprite画像を作る(作り直す)のに手間がかかる
例えばPhotoshopでデザインを行って切り出しを行う場合、CSS Spriteのためだけに別データを作る必要があります。「やっとできた!」と思ったところで画像を変更しなければならなくなった場合、Sprite画像に含める画像が多いほど配置調整と座標計算の手間がかかります。
このデメリットは解消する手段がはあります。後ほど紹介しますが最近はSprite画像をジェネレートできるWebサービスやSass / Compass(CSSをコンパイルできるメタ言語とフレームワーク)を使うことで大幅に手間を省くことができるようになりました。
4.3,メモリ消費に注意
これはCSS Spriteが悪い訳ではなく、10000px × 10000pxのような大きすぎるSprite画像を作ってしまうと余白や使っている色が増えファイルサイズも大きくなり、それがWebサイトの至る所で使われているとレンダリングにおいて大量のメモリを消費してしまうそうです(少し古い情報なのでブラウザによっては改善されているかもしれません)。リクエスト数を削減できたとしてもファイルサイズが大きくなりすぎてしまい画像のロードが遅くなり数秒間真っ白になってしまっては本末転倒です。Sprite画像のファイルサイズにも気を遣い大きくなりすぎないように分割しましょう。
4.4,Sprite画像をインラインイメージ化して良いのか
単純に考えればSprite画像をBase64エンコードしてインラインイメージとすれば完璧なんじゃないか!?と思ってしまうかもしれませんが基本的には併用しない方が良いでしょう。前回で解説しましたが、HTML,CSSのファイルサイズが大きくなりすぎてしまうと、DOMContentLoadedイベントの遅延の原因になります。
5,手間を省くツール
手間を省くにはなんと言ってもSass/Compassを利用するのがお勧めです。
ここでは解説を省かせていただきますがその他にもRuby系のパッケージにいくつかCSS Spriteを自動化できるものがあります。最終的にGruntを使って管理するのが良いでしょう。
5.1,Sass / Compassを使ったCSS Spriteの指定方法
Sass / Compassの使い方やCSS Spriteの指定方法は過去に書いているのでこちらを参考にしてください。
- 【Webデザイナ-・コーダー向け】すぐに使えるSCSS入門|基礎編
- 【Webデザイナ-・コーダー向け】すぐに使えるSCSS入門|Compass基礎編
- コーダー必見、SCSS・Compassで開発効率アップ|Retina Display対応CSS Sprite編
6,パフォーマンス計測
最後にいくつかのサンプル「CSSへのリンクによる埋め込み・インラインイメージ・CSS Sprite」でパフォーマンスを計測してみましょう。今回は高速な通信回線とモバイル通信回線(LTE)を使って2つのイベントが起こるまでの経過時間をチェックします。1つ目はDOMContentLoadedイベント(DOMの構築が終わった段階で呼ばれるイベント)、2つ目がLoadイベント(すべての読み込みが完了した段階で呼ばれるイベント)です。
計測したサンプルはこちらです。
6.1,ファイルサイズ
サンプルのファイルサイズがこちらです。CSS SpriteのSprite画像のファイルサイズが他のものよりも小さいですが Sass/Compass で作られたSprite画像のためこのサイズとなっています。Sass/Compassで作られたSprite画像は若干軽量化されて書き出されるのもうれしいところです。
タイプ | CSSへのリンク による埋め込み |
インラインイメージ | CSS Sprite |
---|---|---|---|
HTML | 640 B | 647 B | 673 B |
CSS | 916 B | 15,398 B | 733 B |
画像 | 14,000 B |
0 B | 5715 B |
6.2,DOMContentLoadedイベント
DOMContentLoadedイベントはどちらの通信回線も大きな差はみられませんでした。
高速な通信回線の場合
タイプ | CSSへのリンク による埋め込み |
インラインイメージ | CSS Sprite |
---|---|---|---|
平均値 | 169.5 ms | 158.8 ms | 171 ms |
1(キャッシュ無し) | 270 | 159 | 277 |
2 | 159 | 154 | 166 |
3 | 153 | 158 | 157 |
4 | 160 | 157 | 155 |
5 | 156 | 167 | 156 |
6 | 159 | 159 | 151 |
7 | 152 | 156 | 174 |
8 | 165 | 161 | 157 |
9 | 164 | 156 | 162 |
10 | 157 | 161 | 155 |
モバイル通信回線の場合
タイプ | CSSへのリンク による埋め込み |
インラインイメージ | CSS Sprite |
---|---|---|---|
平均値 | 895.2 ms | 864.2 ms | 868.4 ms |
1(キャッシュ無し) | 806 | 612 | 1,030 |
2 | 757 | 1,040 | 616 |
3 | 1,020 | 587 | 809 |
4 | 818 | 713 | 778 |
5 | 814 | 788 | 1,040 |
6 | 1,370 | 832 | 789 |
7 | 792 | 1,000 | 996 |
8 | 1,030 | 1,040 | 1,230 |
9 | 764 | 1,000 | 823 |
10 | 781 | 1,030 | 573 |
6.3,Loadイベント
Loadイベントはすべてのリソースの読み込みが完了した段階で呼ばれます。高速な通信回線ではSprite画像を使っていない「CSSへのリンクによる埋め込み」が遅く、インラインイメージとCSS Spriteが同程度のパフォーマンスとなりました。モバイル通信回線についても傾向としては同じようです。
高速な通信回線の場合
タイプ | CSSへのリンク による埋め込み |
インラインイメージ | CSS Sprite |
---|---|---|---|
平均値 | 550.5 ms | 261.3 ms | 280.9 ms |
1(キャッシュ無し) | 979 | 260 | 504 |
2 | 509 | 257 | 265 |
3 | 502 | 261 | 253 |
4 | 498 | 261 | 249 |
5 | 502 | 273 | 249 |
6 | 502 | 262 | 245 |
7 | 495 | 255 | 281 |
8 | 503 | 259 | 253 |
9 | 510 | 256 | 258 |
10 | 505 | 269 | 252 |
モバイル通信回線の場合
タイプ | CSSへのリンク による埋め込み |
インラインイメージ | CSS Sprite |
---|---|---|---|
平均値 | 1,217.9 ms | 1,016.9 ms | 1,031.8 ms |
1(キャッシュ無し) | 2,690 | 769 | 1,210 |
2 | 915 | 1,180 | 814 |
3 | 1,160 | 745 | 962 |
4 | 965 | 867 | 917 |
5 | 966 | 940 | 1,190 |
6 | 1,510 | 978 | 933 |
7 | 944 | 1,150 | 1,150 |
8 | 1,190 | 1,180 | 1,430 |
9 | 910 | 1,170 | 972 |
10 | 929 | 1,190 | 740 |
まとめ
CSS Spriteの紹介は以上となります。インラインイメージと比べるとCSS Spriteは積極的に使え、効果も高いテクニックです。
キャッシュも効きますし、HTMLのロードにも大きな影響も与えないため自動化さえできれば大きくリクエスト数を削減することができます。特にこのような画像には有効なテクニックです。
- ボタンやナビゲーションなどのアフォーダンス毎に画像を切り替える場合
- アイコンなどの細かい画像
- セマンティックを考えたときにimgタグを使う価値のない装飾に使われている画像
次回はWebフォントアイコンについて紹介します。